/*
 * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
/*
 * Copyright 2001-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.sun.org.apache.xerces.internal.impl.xs.traversers;

import java.util.Locale;
import java.util.Vector;

import com.sun.org.apache.xerces.internal.impl.dv.InvalidDatatypeValueException;
import com.sun.org.apache.xerces.internal.impl.dv.XSFacets;
import com.sun.org.apache.xerces.internal.impl.dv.XSSimpleType;
import com.sun.org.apache.xerces.internal.impl.validation.ValidationState;
import com.sun.org.apache.xerces.internal.impl.xs.SchemaGrammar;
import com.sun.org.apache.xerces.internal.impl.xs.SchemaSymbols;
import com.sun.org.apache.xerces.internal.impl.xs.XSAnnotationImpl;
import com.sun.org.apache.xerces.internal.impl.xs.XSAttributeGroupDecl;
import com.sun.org.apache.xerces.internal.impl.xs.XSAttributeUseImpl;
import com.sun.org.apache.xerces.internal.impl.xs.XSComplexTypeDecl;
import com.sun.org.apache.xerces.internal.impl.xs.XSElementDecl;
import com.sun.org.apache.xerces.internal.impl.xs.XSParticleDecl;
import com.sun.org.apache.xerces.internal.impl.xs.XSWildcardDecl;
import com.sun.org.apache.xerces.internal.impl.xs.util.XInt;
import com.sun.org.apache.xerces.internal.impl.xs.util.XSObjectListImpl;
import com.sun.org.apache.xerces.internal.util.DOMUtil;
import com.sun.org.apache.xerces.internal.util.NamespaceSupport;
import com.sun.org.apache.xerces.internal.util.SymbolTable;
import com.sun.org.apache.xerces.internal.xni.QName;
import com.sun.org.apache.xerces.internal.xs.XSAttributeUse;
import com.sun.org.apache.xerces.internal.xs.XSObjectList;
import com.sun.org.apache.xerces.internal.xs.XSTypeDefinition;
import org.w3c.dom.Element;

/**
 * Class <code>XSDAbstractTraverser</code> serves as the base class for all
 * other <code>XSD???Traverser</code>s. It holds the common data and provide
 * a unified way to initialize these data.
 *
 * @author Elena Litani, IBM
 * @author Rahul Srivastava, Sun Microsystems Inc.
 * @author Neeraj Bajaj, Sun Microsystems Inc.
 * @version $Id: XSDAbstractTraverser.java,v 1.8 2010-11-01 04:40:02 joehw Exp $
 * @xerces.internal
 */
abstract class XSDAbstractTraverser {

  protected static final String NO_NAME = "(no name)";

  // Flags for checkOccurrences to indicate any special
  // restrictions on minOccurs and maxOccurs relating to "all".
  //    NOT_ALL_CONTEXT    - not processing an <all>
  //    PROCESSING_ALL_EL  - processing an <element> in an <all>
  //    GROUP_REF_WITH_ALL - processing <group> reference that contained <all>
  //    CHILD_OF_GROUP     - processing a child of a model group definition
  //    PROCESSING_ALL_GP  - processing an <all> group itself

  protected static final int NOT_ALL_CONTEXT = 0;
  protected static final int PROCESSING_ALL_EL = 1;
  protected static final int GROUP_REF_WITH_ALL = 2;
  protected static final int CHILD_OF_GROUP = 4;
  protected static final int PROCESSING_ALL_GP = 8;

  //Shared data
  protected XSDHandler fSchemaHandler = null;
  protected SymbolTable fSymbolTable = null;
  protected XSAttributeChecker fAttrChecker = null;
  protected boolean fValidateAnnotations = false;

  // used to validate default/fixed attribute values
  ValidationState fValidationState = new ValidationState();

  XSDAbstractTraverser(XSDHandler handler,
      XSAttributeChecker attrChecker) {
    fSchemaHandler = handler;
    fAttrChecker = attrChecker;
  }

  void reset(SymbolTable symbolTable, boolean validateAnnotations, Locale locale) {
    fSymbolTable = symbolTable;
    fValidateAnnotations = validateAnnotations;
    fValidationState.setExtraChecking(false);
    fValidationState.setSymbolTable(symbolTable);
    fValidationState.setLocale(locale);
  }

  // traverse the annotation declaration
  // REVISIT: how to pass the parentAttrs? as DOM attributes?
  //          as name/value pairs (string)? in parsed form?
  // @return XSAnnotationImpl object
  XSAnnotationImpl traverseAnnotationDecl(Element annotationDecl, Object[] parentAttrs,
      boolean isGlobal, XSDocumentInfo schemaDoc) {
    // General Attribute Checking
    Object[] attrValues = fAttrChecker.checkAttributes(annotationDecl, isGlobal, schemaDoc);
    fAttrChecker.returnAttrArray(attrValues, schemaDoc);

    String contents = DOMUtil.getAnnotation(annotationDecl);
    Element child = DOMUtil.getFirstChildElement(annotationDecl);
    if (child != null) {
      do {
        String name = DOMUtil.getLocalName(child);

        // the only valid children of "annotation" are
        // "appinfo" and "documentation"
        if (!((name.equals(SchemaSymbols.ELT_APPINFO)) ||
            (name.equals(SchemaSymbols.ELT_DOCUMENTATION)))) {
          reportSchemaError("src-annotation", new Object[]{name}, child);
        } else {
          // General Attribute Checking
          // There is no difference between global or local appinfo/documentation,
          // so we assume it's always global.
          attrValues = fAttrChecker.checkAttributes(child, true, schemaDoc);
          fAttrChecker.returnAttrArray(attrValues, schemaDoc);
        }

        child = DOMUtil.getNextSiblingElement(child);
      }
      while (child != null);
    }
    // if contents was null, must have been some kind of error;
    // nothing to contribute to PSVI
    if (contents == null) {
      return null;
    }

    // find the grammar; fSchemaHandler must be known!
    SchemaGrammar grammar = fSchemaHandler.getGrammar(schemaDoc.fTargetNamespace);
    // fish out local attributes passed from parent
    Vector annotationLocalAttrs = (Vector) parentAttrs[XSAttributeChecker.ATTIDX_NONSCHEMA];
    // optimize for case where there are no local attributes
    if (annotationLocalAttrs != null && !annotationLocalAttrs.isEmpty()) {
      StringBuffer localStrBuffer = new StringBuffer(64);
      localStrBuffer.append(" ");
      // Vector should contain rawname value pairs
      int i = 0;
      while (i < annotationLocalAttrs.size()) {
        String rawname = (String) annotationLocalAttrs.elementAt(i++);
        int colonIndex = rawname.indexOf(':');
        String prefix, localpart;
        if (colonIndex == -1) {
          prefix = "";
          localpart = rawname;
        } else {
          prefix = rawname.substring(0, colonIndex);
          localpart = rawname.substring(colonIndex + 1);
        }
        String uri = schemaDoc.fNamespaceSupport.getURI(fSymbolTable.addSymbol(prefix));
        if (annotationDecl.getAttributeNS(uri, localpart).length() != 0) {
          i++; // skip the next value, too
          continue;
        }
        localStrBuffer.append(rawname)
            .append("=\"");
        String value = (String) annotationLocalAttrs.elementAt(i++);
        // search for pesky "s and <s within attr value:
        value = processAttValue(value);
        localStrBuffer.append(value)
            .append("\" ");
      }
      // and now splice it into place; immediately after the annotation token, for simplicity's sake
      StringBuffer contentBuffer = new StringBuffer(contents.length() + localStrBuffer.length());
      int annotationTokenEnd = contents.indexOf(SchemaSymbols.ELT_ANNOTATION);
      // annotation must occur somewhere or we're in big trouble...
      if (annotationTokenEnd == -1) {
        return null;
      }
      annotationTokenEnd += SchemaSymbols.ELT_ANNOTATION.length();
      contentBuffer.append(contents.substring(0, annotationTokenEnd));
      contentBuffer.append(localStrBuffer.toString());
      contentBuffer.append(contents.substring(annotationTokenEnd, contents.length()));
      final String annotation = contentBuffer.toString();
      if (fValidateAnnotations) {
        schemaDoc.addAnnotation(new XSAnnotationInfo(annotation, annotationDecl));
      }
      return new XSAnnotationImpl(annotation, grammar);
    } else {
      if (fValidateAnnotations) {
        schemaDoc.addAnnotation(new XSAnnotationInfo(contents, annotationDecl));
      }
      return new XSAnnotationImpl(contents, grammar);
    }

  }

  XSAnnotationImpl traverseSyntheticAnnotation(Element annotationParent, String initialContent,
      Object[] parentAttrs, boolean isGlobal, XSDocumentInfo schemaDoc) {

    String contents = initialContent;

    // find the grammar; fSchemaHandler must be known!
    SchemaGrammar grammar = fSchemaHandler.getGrammar(schemaDoc.fTargetNamespace);
    // fish out local attributes passed from parent
    Vector annotationLocalAttrs = (Vector) parentAttrs[XSAttributeChecker.ATTIDX_NONSCHEMA];
    // optimize for case where there are no local attributes
    if (annotationLocalAttrs != null && !annotationLocalAttrs.isEmpty()) {
      StringBuffer localStrBuffer = new StringBuffer(64);
      localStrBuffer.append(" ");
      // Vector should contain rawname value pairs
      int i = 0;
      while (i < annotationLocalAttrs.size()) {
        String rawname = (String) annotationLocalAttrs.elementAt(i++);
        int colonIndex = rawname.indexOf(':');
        String prefix, localpart;
        if (colonIndex == -1) {
          prefix = "";
          localpart = rawname;
        } else {
          prefix = rawname.substring(0, colonIndex);
          localpart = rawname.substring(colonIndex + 1);
        }
        String uri = schemaDoc.fNamespaceSupport.getURI(fSymbolTable.addSymbol(prefix));
        localStrBuffer.append(rawname)
            .append("=\"");
        String value = (String) annotationLocalAttrs.elementAt(i++);
        // search for pesky "s and <s within attr value:
        value = processAttValue(value);
        localStrBuffer.append(value)
            .append("\" ");
      }
      // and now splice it into place; immediately after the annotation token, for simplicity's sake
      StringBuffer contentBuffer = new StringBuffer(contents.length() + localStrBuffer.length());
      int annotationTokenEnd = contents.indexOf(SchemaSymbols.ELT_ANNOTATION);
      // annotation must occur somewhere or we're in big trouble...
      if (annotationTokenEnd == -1) {
        return null;
      }
      annotationTokenEnd += SchemaSymbols.ELT_ANNOTATION.length();
      contentBuffer.append(contents.substring(0, annotationTokenEnd));
      contentBuffer.append(localStrBuffer.toString());
      contentBuffer.append(contents.substring(annotationTokenEnd, contents.length()));
      final String annotation = contentBuffer.toString();
      if (fValidateAnnotations) {
        schemaDoc.addAnnotation(new XSAnnotationInfo(annotation, annotationParent));
      }
      return new XSAnnotationImpl(annotation, grammar);
    } else {
      if (fValidateAnnotations) {
        schemaDoc.addAnnotation(new XSAnnotationInfo(contents, annotationParent));
      }
      return new XSAnnotationImpl(contents, grammar);
    }
  }

  // the QName simple type used to resolve qnames
  private static final XSSimpleType fQNameDV = (XSSimpleType) SchemaGrammar.SG_SchemaNS
      .getGlobalTypeDecl(SchemaSymbols.ATTVAL_QNAME);
  // Temp data structures to be re-used in traversing facets
  private StringBuffer fPattern = new StringBuffer();
  private final XSFacets xsFacets = new XSFacets();

  static final class FacetInfo {

    final XSFacets facetdata;
    final Element nodeAfterFacets;
    final short fPresentFacets;
    final short fFixedFacets;

    FacetInfo(XSFacets facets, Element nodeAfterFacets, short presentFacets, short fixedFacets) {
      facetdata = facets;
      this.nodeAfterFacets = nodeAfterFacets;
      fPresentFacets = presentFacets;
      fFixedFacets = fixedFacets;
    }
  }

  FacetInfo traverseFacets(Element content,
      XSSimpleType baseValidator,
      XSDocumentInfo schemaDoc) {

    short facetsPresent = 0;
    short facetsFixed = 0; // facets that have fixed="true"
    String facet;
    boolean hasQName = containsQName(baseValidator);
    Vector enumData = null;
    XSObjectListImpl enumAnnotations = null;
    XSObjectListImpl patternAnnotations = null;
    Vector enumNSDecls = hasQName ? new Vector() : null;
    int currentFacet = 0;
    xsFacets.reset();
    while (content != null) {
      // General Attribute Checking
      Object[] attrs = null;
      facet = DOMUtil.getLocalName(content);
      if (facet.equals(SchemaSymbols.ELT_ENUMERATION)) {
        attrs = fAttrChecker.checkAttributes(content, false, schemaDoc, hasQName);
        String enumVal = (String) attrs[XSAttributeChecker.ATTIDX_VALUE];
        // The facet can't be used if the value is missing. Ignore
        // this facet element.
        if (enumVal == null) {
          reportSchemaError("s4s-att-must-appear",
              new Object[]{SchemaSymbols.ELT_ENUMERATION, SchemaSymbols.ATT_VALUE}, content);
          fAttrChecker.returnAttrArray(attrs, schemaDoc);
          content = DOMUtil.getNextSiblingElement(content);
          continue;
        }

        NamespaceSupport nsDecls = (NamespaceSupport) attrs[XSAttributeChecker.ATTIDX_ENUMNSDECLS];

        // for NOTATION types, need to check whether there is a notation
        // declared with the same name as the enumeration value.
        if (baseValidator.getVariety() == XSSimpleType.VARIETY_ATOMIC &&
            baseValidator.getPrimitiveKind() == XSSimpleType.PRIMITIVE_NOTATION) {
          // need to use the namespace context returned from checkAttributes
          schemaDoc.fValidationContext.setNamespaceSupport(nsDecls);
          Object notation = null;
          try {
            QName temp = (QName) fQNameDV.validate(enumVal, schemaDoc.fValidationContext, null);
            // try to get the notation decl. if failed, getGlobalDecl
            // reports an error, so we don't need to report one again.
            notation = fSchemaHandler
                .getGlobalDecl(schemaDoc, XSDHandler.NOTATION_TYPE, temp, content);
          } catch (InvalidDatatypeValueException ex) {
            reportSchemaError(ex.getKey(), ex.getArgs(), content);
          }
          if (notation == null) {
            // Either the QName value is invalid, or it doens't
            // resolve to a notation declaration.
            // Ignore this facet, to avoid instance validation problems
            fAttrChecker.returnAttrArray(attrs, schemaDoc);
            content = DOMUtil.getNextSiblingElement(content);
            continue;
          }
          // restore to the normal namespace context
          schemaDoc.fValidationContext.setNamespaceSupport(schemaDoc.fNamespaceSupport);
        }
        if (enumData == null) {
          enumData = new Vector();
          enumAnnotations = new XSObjectListImpl();
        }
        enumData.addElement(enumVal);
        enumAnnotations.addXSObject(null);
        if (hasQName) {
          enumNSDecls.addElement(nsDecls);
        }
        Element child = DOMUtil.getFirstChildElement(content);

        if (child != null &&
            DOMUtil.getLocalName(child).equals(SchemaSymbols.ELT_ANNOTATION)) {
          // traverse annotation if any
          enumAnnotations.addXSObject(enumAnnotations.getLength() - 1,
              traverseAnnotationDecl(child, attrs, false, schemaDoc));
          child = DOMUtil.getNextSiblingElement(child);
        } else {
          String text = DOMUtil.getSyntheticAnnotation(content);
          if (text != null) {
            enumAnnotations.addXSObject(enumAnnotations.getLength() - 1,
                traverseSyntheticAnnotation(content, text, attrs, false, schemaDoc));
          }
        }
        if (child != null) {
          reportSchemaError("s4s-elt-must-match.1",
              new Object[]{"enumeration", "(annotation?)", DOMUtil.getLocalName(child)}, child);
        }
      } else if (facet.equals(SchemaSymbols.ELT_PATTERN)) {
        facetsPresent |= XSSimpleType.FACET_PATTERN;
        attrs = fAttrChecker.checkAttributes(content, false, schemaDoc);
        String patternVal = (String) attrs[XSAttributeChecker.ATTIDX_VALUE];
        // The facet can't be used if the value is missing. Ignore
        // this facet element.
        if (patternVal == null) {
          reportSchemaError("s4s-att-must-appear",
              new Object[]{SchemaSymbols.ELT_PATTERN, SchemaSymbols.ATT_VALUE}, content);
          fAttrChecker.returnAttrArray(attrs, schemaDoc);
          content = DOMUtil.getNextSiblingElement(content);
          continue;
        }

        if (fPattern.length() == 0) {
          fPattern.append(patternVal);
        } else {
          // ---------------------------------------------
          //datatypes: 5.2.4 pattern: src-multiple-pattern
          // ---------------------------------------------
          fPattern.append("|");
          fPattern.append(patternVal);
        }
        Element child = DOMUtil.getFirstChildElement(content);
        if (child != null &&
            DOMUtil.getLocalName(child).equals(SchemaSymbols.ELT_ANNOTATION)) {
          // traverse annotation if any
          if (patternAnnotations == null) {
            patternAnnotations = new XSObjectListImpl();
          }
          patternAnnotations.addXSObject(traverseAnnotationDecl(child, attrs, false, schemaDoc));
          child = DOMUtil.getNextSiblingElement(child);
        } else {
          String text = DOMUtil.getSyntheticAnnotation(content);
          if (text != null) {
            if (patternAnnotations == null) {
              patternAnnotations = new XSObjectListImpl();
            }
            patternAnnotations
                .addXSObject(traverseSyntheticAnnotation(content, text, attrs, false, schemaDoc));
          }
        }
        if (child != null) {
          reportSchemaError("s4s-elt-must-match.1",
              new Object[]{"pattern", "(annotation?)", DOMUtil.getLocalName(child)}, child);
        }
      } else {
        if (facet.equals(SchemaSymbols.ELT_MINLENGTH)) {
          currentFacet = XSSimpleType.FACET_MINLENGTH;
        } else if (facet.equals(SchemaSymbols.ELT_MAXLENGTH)) {
          currentFacet = XSSimpleType.FACET_MAXLENGTH;
        } else if (facet.equals(SchemaSymbols.ELT_MAXEXCLUSIVE)) {
          currentFacet = XSSimpleType.FACET_MAXEXCLUSIVE;
        } else if (facet.equals(SchemaSymbols.ELT_MAXINCLUSIVE)) {
          currentFacet = XSSimpleType.FACET_MAXINCLUSIVE;
        } else if (facet.equals(SchemaSymbols.ELT_MINEXCLUSIVE)) {
          currentFacet = XSSimpleType.FACET_MINEXCLUSIVE;
        } else if (facet.equals(SchemaSymbols.ELT_MININCLUSIVE)) {
          currentFacet = XSSimpleType.FACET_MININCLUSIVE;
        } else if (facet.equals(SchemaSymbols.ELT_TOTALDIGITS)) {
          currentFacet = XSSimpleType.FACET_TOTALDIGITS;
        } else if (facet.equals(SchemaSymbols.ELT_FRACTIONDIGITS)) {
          currentFacet = XSSimpleType.FACET_FRACTIONDIGITS;
        } else if (facet.equals(SchemaSymbols.ELT_WHITESPACE)) {
          currentFacet = XSSimpleType.FACET_WHITESPACE;
        } else if (facet.equals(SchemaSymbols.ELT_LENGTH)) {
          currentFacet = XSSimpleType.FACET_LENGTH;
        } else {
          break;   // a non-facet
        }

        attrs = fAttrChecker.checkAttributes(content, false, schemaDoc);

        // check for duplicate facets
        if ((facetsPresent & currentFacet) != 0) {
          // Ignore this facet, to avoid corrupting the previous facet
          reportSchemaError("src-single-facet-value", new Object[]{facet}, content);
          fAttrChecker.returnAttrArray(attrs, schemaDoc);
          content = DOMUtil.getNextSiblingElement(content);
          continue;
        }

        // The facet can't be used if the value is missing. Ignore
        // this facet element.
        if (attrs[XSAttributeChecker.ATTIDX_VALUE] == null) {
          // Report an error if the "value" attribute is missing.
          // If it's not missing, then its value is invalid, and an
          // error should have already been reported by the
          // attribute checker.
          if (content.getAttributeNodeNS(null, "value") == null) {
            reportSchemaError("s4s-att-must-appear",
                new Object[]{content.getLocalName(), SchemaSymbols.ATT_VALUE}, content);
          }
          fAttrChecker.returnAttrArray(attrs, schemaDoc);
          content = DOMUtil.getNextSiblingElement(content);
          continue;
        }

        facetsPresent |= currentFacet;
        // check for fixed facet
        if (((Boolean) attrs[XSAttributeChecker.ATTIDX_FIXED]).booleanValue()) {
          facetsFixed |= currentFacet;
        }
        switch (currentFacet) {
          case XSSimpleType.FACET_MINLENGTH:
            xsFacets.minLength = ((XInt) attrs[XSAttributeChecker.ATTIDX_VALUE]).intValue();
            break;
          case XSSimpleType.FACET_MAXLENGTH:
            xsFacets.maxLength = ((XInt) attrs[XSAttributeChecker.ATTIDX_VALUE]).intValue();
            break;
          case XSSimpleType.FACET_MAXEXCLUSIVE:
            xsFacets.maxExclusive = (String) attrs[XSAttributeChecker.ATTIDX_VALUE];
            break;
          case XSSimpleType.FACET_MAXINCLUSIVE:
            xsFacets.maxInclusive = (String) attrs[XSAttributeChecker.ATTIDX_VALUE];
            break;
          case XSSimpleType.FACET_MINEXCLUSIVE:
            xsFacets.minExclusive = (String) attrs[XSAttributeChecker.ATTIDX_VALUE];
            break;
          case XSSimpleType.FACET_MININCLUSIVE:
            xsFacets.minInclusive = (String) attrs[XSAttributeChecker.ATTIDX_VALUE];
            break;
          case XSSimpleType.FACET_TOTALDIGITS:
            xsFacets.totalDigits = ((XInt) attrs[XSAttributeChecker.ATTIDX_VALUE]).intValue();
            break;
          case XSSimpleType.FACET_FRACTIONDIGITS:
            xsFacets.fractionDigits = ((XInt) attrs[XSAttributeChecker.ATTIDX_VALUE]).intValue();
            break;
          case XSSimpleType.FACET_WHITESPACE:
            xsFacets.whiteSpace = ((XInt) attrs[XSAttributeChecker.ATTIDX_VALUE]).shortValue();
            break;
          case XSSimpleType.FACET_LENGTH:
            xsFacets.length = ((XInt) attrs[XSAttributeChecker.ATTIDX_VALUE]).intValue();
            break;
        }

        Element child = DOMUtil.getFirstChildElement(content);
        XSAnnotationImpl annotation = null;
        if (child != null &&
            DOMUtil.getLocalName(child).equals(SchemaSymbols.ELT_ANNOTATION)) {
          // traverse annotation if any
          annotation = traverseAnnotationDecl(child, attrs, false, schemaDoc);
          child = DOMUtil.getNextSiblingElement(child);
        } else {
          String text = DOMUtil.getSyntheticAnnotation(content);
          if (text != null) {
            annotation = traverseSyntheticAnnotation(content, text, attrs, false, schemaDoc);
          }
        }
        switch (currentFacet) {
          case XSSimpleType.FACET_MINLENGTH:
            xsFacets.minLengthAnnotation = annotation;
            break;
          case XSSimpleType.FACET_MAXLENGTH:
            xsFacets.maxLengthAnnotation = annotation;
            break;
          case XSSimpleType.FACET_MAXEXCLUSIVE:
            xsFacets.maxExclusiveAnnotation = annotation;
            break;
          case XSSimpleType.FACET_MAXINCLUSIVE:
            xsFacets.maxInclusiveAnnotation = annotation;
            break;
          case XSSimpleType.FACET_MINEXCLUSIVE:
            xsFacets.minExclusiveAnnotation = annotation;
            break;
          case XSSimpleType.FACET_MININCLUSIVE:
            xsFacets.minInclusiveAnnotation = annotation;
            break;
          case XSSimpleType.FACET_TOTALDIGITS:
            xsFacets.totalDigitsAnnotation = annotation;
            break;
          case XSSimpleType.FACET_FRACTIONDIGITS:
            xsFacets.fractionDigitsAnnotation = annotation;
            break;
          case XSSimpleType.FACET_WHITESPACE:
            xsFacets.whiteSpaceAnnotation = annotation;
            break;
          case XSSimpleType.FACET_LENGTH:
            xsFacets.lengthAnnotation = annotation;
            break;
        }
        if (child != null) {
          reportSchemaError("s4s-elt-must-match.1",
              new Object[]{facet, "(annotation?)", DOMUtil.getLocalName(child)}, child);
        }
      }
      fAttrChecker.returnAttrArray(attrs, schemaDoc);
      content = DOMUtil.getNextSiblingElement(content);
    }
    if (enumData != null) {
      facetsPresent |= XSSimpleType.FACET_ENUMERATION;
      xsFacets.enumeration = enumData;
      xsFacets.enumNSDecls = enumNSDecls;
      xsFacets.enumAnnotations = enumAnnotations;
    }
    if ((facetsPresent & XSSimpleType.FACET_PATTERN) != 0) {
      xsFacets.pattern = fPattern.toString();
      xsFacets.patternAnnotations = patternAnnotations;
    }

    fPattern.setLength(0);

    return new FacetInfo(xsFacets, content, facetsPresent, facetsFixed);
  }


  // return whether QName/NOTATION is part of the given type
  private boolean containsQName(XSSimpleType type) {
    if (type.getVariety() == XSSimpleType.VARIETY_ATOMIC) {
      short primitive = type.getPrimitiveKind();
      return (primitive == XSSimpleType.PRIMITIVE_QNAME ||
          primitive == XSSimpleType.PRIMITIVE_NOTATION);
    } else if (type.getVariety() == XSSimpleType.VARIETY_LIST) {
      return containsQName((XSSimpleType) type.getItemType());
    } else if (type.getVariety() == XSSimpleType.VARIETY_UNION) {
      XSObjectList members = type.getMemberTypes();
      for (int i = 0; i < members.getLength(); i++) {
        if (containsQName((XSSimpleType) members.item(i))) {
          return true;
        }
      }
    }
    return false;
  }

  //
  // Traverse a set of attribute and attribute group elements
  // Needed by complexType and attributeGroup traversal
  // This method will return the first non-attribute/attrgrp found
  //
  Element traverseAttrsAndAttrGrps(Element firstAttr, XSAttributeGroupDecl attrGrp,
      XSDocumentInfo schemaDoc, SchemaGrammar grammar,
      XSComplexTypeDecl enclosingCT) {

    Element child = null;
    XSAttributeGroupDecl tempAttrGrp = null;
    XSAttributeUseImpl tempAttrUse = null;
    XSAttributeUse otherUse = null;
    String childName;

    for (child = firstAttr; child != null; child = DOMUtil.getNextSiblingElement(child)) {
      childName = DOMUtil.getLocalName(child);
      if (childName.equals(SchemaSymbols.ELT_ATTRIBUTE)) {
        tempAttrUse = fSchemaHandler.fAttributeTraverser.traverseLocal(child,
            schemaDoc,
            grammar,
            enclosingCT);
        if (tempAttrUse == null) {
          continue;
        }
        if (tempAttrUse.fUse == SchemaSymbols.USE_PROHIBITED) {
          attrGrp.addAttributeUse(tempAttrUse);
          continue;
        }
        otherUse = attrGrp.getAttributeUseNoProhibited(
            tempAttrUse.fAttrDecl.getNamespace(),
            tempAttrUse.fAttrDecl.getName());
        if (otherUse == null) {
          String idName = attrGrp.addAttributeUse(tempAttrUse);
          if (idName != null) {
            String code = (enclosingCT == null) ? "ag-props-correct.3" : "ct-props-correct.5";
            String name = (enclosingCT == null) ? attrGrp.fName : enclosingCT.getName();
            reportSchemaError(code, new Object[]{name, tempAttrUse.fAttrDecl.getName(), idName},
                child);
          }
        } else if (otherUse != tempAttrUse) {
          String code = (enclosingCT == null) ? "ag-props-correct.2" : "ct-props-correct.4";
          String name = (enclosingCT == null) ? attrGrp.fName : enclosingCT.getName();
          reportSchemaError(code, new Object[]{name, tempAttrUse.fAttrDecl.getName()}, child);
        }
      } else if (childName.equals(SchemaSymbols.ELT_ATTRIBUTEGROUP)) {
        //REVISIT: do we need to save some state at this point??
        tempAttrGrp = fSchemaHandler.fAttributeGroupTraverser.traverseLocal(
            child, schemaDoc, grammar);
        if (tempAttrGrp == null) {
          continue;
        }
        XSObjectList attrUseS = tempAttrGrp.getAttributeUses();
        XSAttributeUseImpl oneAttrUse;
        int attrCount = attrUseS.getLength();
        for (int i = 0; i < attrCount; i++) {
          oneAttrUse = (XSAttributeUseImpl) attrUseS.item(i);
          if (oneAttrUse.fUse == SchemaSymbols.USE_PROHIBITED) {
            attrGrp.addAttributeUse(oneAttrUse);
            continue;
          }
          otherUse = attrGrp.getAttributeUseNoProhibited(
              oneAttrUse.fAttrDecl.getNamespace(),
              oneAttrUse.fAttrDecl.getName());
          if (otherUse == null) {
            String idName = attrGrp.addAttributeUse(oneAttrUse);
            if (idName != null) {
              String code = (enclosingCT == null) ? "ag-props-correct.3" : "ct-props-correct.5";
              String name = (enclosingCT == null) ? attrGrp.fName : enclosingCT.getName();
              reportSchemaError(code, new Object[]{name, oneAttrUse.fAttrDecl.getName(), idName},
                  child);
            }
          } else if (oneAttrUse != otherUse) {
            String code = (enclosingCT == null) ? "ag-props-correct.2" : "ct-props-correct.4";
            String name = (enclosingCT == null) ? attrGrp.fName : enclosingCT.getName();
            reportSchemaError(code, new Object[]{name, oneAttrUse.fAttrDecl.getName()}, child);
          }
        }

        if (tempAttrGrp.fAttributeWC != null) {
          if (attrGrp.fAttributeWC == null) {
            attrGrp.fAttributeWC = tempAttrGrp.fAttributeWC;
          }
          // perform intersection of attribute wildcard
          else {
            attrGrp.fAttributeWC = attrGrp.fAttributeWC.
                performIntersectionWith(tempAttrGrp.fAttributeWC,
                    attrGrp.fAttributeWC.fProcessContents);
            if (attrGrp.fAttributeWC == null) {
              String code = (enclosingCT == null) ? "src-attribute_group.2" : "src-ct.4";
              String name = (enclosingCT == null) ? attrGrp.fName : enclosingCT.getName();
              reportSchemaError(code, new Object[]{name}, child);
            }
          }
        }
      } else {
        break;
      }
    } // for

    if (child != null) {
      childName = DOMUtil.getLocalName(child);
      if (childName.equals(SchemaSymbols.ELT_ANYATTRIBUTE)) {
        XSWildcardDecl tempAttrWC = fSchemaHandler.fWildCardTraverser.
            traverseAnyAttribute(child, schemaDoc, grammar);
        if (attrGrp.fAttributeWC == null) {
          attrGrp.fAttributeWC = tempAttrWC;
        }
        // perform intersection of attribute wildcard
        else {
          attrGrp.fAttributeWC = tempAttrWC.
              performIntersectionWith(attrGrp.fAttributeWC, tempAttrWC.fProcessContents);
          if (attrGrp.fAttributeWC == null) {
            String code = (enclosingCT == null) ? "src-attribute_group.2" : "src-ct.4";
            String name = (enclosingCT == null) ? attrGrp.fName : enclosingCT.getName();
            reportSchemaError(code, new Object[]{name}, child);
          }
        }
        child = DOMUtil.getNextSiblingElement(child);
      }
    }

    // Success
    return child;

  }

  void reportSchemaError(String key, Object[] args, Element ele) {
    fSchemaHandler.reportSchemaError(key, args, ele);
  }

  /**
   * Element/Attribute traversers call this method to check whether
   * the type is NOTATION without enumeration facet
   */
  void checkNotationType(String refName, XSTypeDefinition typeDecl, Element elem) {
    if (typeDecl.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE &&
        ((XSSimpleType) typeDecl).getVariety() == XSSimpleType.VARIETY_ATOMIC &&
        ((XSSimpleType) typeDecl).getPrimitiveKind() == XSSimpleType.PRIMITIVE_NOTATION) {
      if ((((XSSimpleType) typeDecl).getDefinedFacets() & XSSimpleType.FACET_ENUMERATION) == 0) {
        reportSchemaError("enumeration-required-notation",
            new Object[]{typeDecl.getName(), refName, DOMUtil.getLocalName(elem)}, elem);
      }
    }
  }

  // Checks constraints for minOccurs, maxOccurs
  protected XSParticleDecl checkOccurrences(XSParticleDecl particle,
      String particleName, Element parent,
      int allContextFlags,
      long defaultVals) {

    int min = particle.fMinOccurs;
    int max = particle.fMaxOccurs;
    boolean defaultMin = (defaultVals & (1 << XSAttributeChecker.ATTIDX_MINOCCURS)) != 0;
    boolean defaultMax = (defaultVals & (1 << XSAttributeChecker.ATTIDX_MAXOCCURS)) != 0;

    boolean processingAllEl = ((allContextFlags & PROCESSING_ALL_EL) != 0);
    boolean processingAllGP = ((allContextFlags & PROCESSING_ALL_GP) != 0);
    boolean groupRefWithAll = ((allContextFlags & GROUP_REF_WITH_ALL) != 0);
    boolean isGroupChild = ((allContextFlags & CHILD_OF_GROUP) != 0);

    // Neither minOccurs nor maxOccurs may be specified
    // for the child of a model group definition.
    if (isGroupChild) {
      if (!defaultMin) {
        Object[] args = new Object[]{particleName, "minOccurs"};
        reportSchemaError("s4s-att-not-allowed", args, parent);
        min = 1;
      }
      if (!defaultMax) {
        Object[] args = new Object[]{particleName, "maxOccurs"};
        reportSchemaError("s4s-att-not-allowed", args, parent);
        max = 1;
      }
    }

    // If minOccurs=maxOccurs=0, no component is specified
    if (min == 0 && max == 0) {
      particle.fType = XSParticleDecl.PARTICLE_EMPTY;
      return null;
    }

    // For the elements referenced in an <all>, minOccurs attribute
    // must be zero or one, and maxOccurs attribute must be one.
    // For a complex type definition that contains an <all> or a
    // reference a <group> whose model group is an all model group,
    // minOccurs and maxOccurs must be one.
    if (processingAllEl) {
      if (max != 1) {
        reportSchemaError("cos-all-limited.2", new Object[]{
            (max == SchemaSymbols.OCCURRENCE_UNBOUNDED) ? SchemaSymbols.ATTVAL_UNBOUNDED
                : Integer.toString(max),
            ((XSElementDecl) particle.fValue).getName()}, parent);
        max = 1;
        if (min > 1) {
          min = 1;
        }
      }
    } else if (processingAllGP || groupRefWithAll) {
      if (max != 1) {
        reportSchemaError("cos-all-limited.1.2", null, parent);
        if (min > 1) {
          min = 1;
        }
        max = 1;
      }
    }

    particle.fMinOccurs = min;
    particle.fMaxOccurs = max;

    return particle;
  }

  private static String processAttValue(String original) {
    final int length = original.length();
    // normally, nothing will happen
    for (int i = 0; i < length; ++i) {
      char currChar = original.charAt(i);
      if (currChar == '"' || currChar == '<' || currChar == '&' ||
          currChar == 0x09 || currChar == 0x0A || currChar == 0x0D) {
        return escapeAttValue(original, i);
      }
    }
    return original;
  }

  // this is not terribly performant!
  private static String escapeAttValue(String original, int from) {
    int i;
    final int length = original.length();
    StringBuffer newVal = new StringBuffer(length);
    newVal.append(original.substring(0, from));
    for (i = from; i < length; ++i) {
      char currChar = original.charAt(i);
      if (currChar == '"') {
        newVal.append("&quot;");
      } else if (currChar == '<') {
        newVal.append("&lt;");
      } else if (currChar == '&') {
        newVal.append("&amp;");
      }
      // Must escape 0x09, 0x0A and 0x0D if they appear in attribute
      // value so that they may be round-tripped. They would otherwise
      // be transformed to a 0x20 during attribute value normalization.
      else if (currChar == 0x09) {
        newVal.append("&#x9;");
      } else if (currChar == 0x0A) {
        newVal.append("&#xA;");
      } else if (currChar == 0x0D) {
        newVal.append("&#xD;");
      } else {
        newVal.append(currChar);
      }
    }
    return newVal.toString();
  }
}
